<!-- build time:Tue Nov 03 2020 20:13:58 GMT+0800 (GMT+08:00) --><!DOCTYPE html><html class="theme-next pisces use-motion" lang="zh-CN"><head><meta name="generator" content="Hexo 3.9.0"><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/Han/3.3.0/han.min.css"><meta name="google-site-verification" content="U349XOYHFCgkEOYhTP4YlFqqyEq_f7zA8B4ERY7tWXA"><meta name="baidu-site-verification" content="APKb23AsyK"><link rel="stylesheet" href="//cdn.bootcss.com/fancybox/3.5.6/jquery.fancybox.min.css"><link rel="stylesheet" href="/blog/lib/font-awesome/css/font-awesome.min.css?v=4.6.2"><link rel="stylesheet" href="/blog/css/main.css?v=7.1.2"><link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png?v=7.1.2"><link rel="icon" type="image/png" sizes="32x32" href="/blog/icos/favicon-48x48.ico?v=7.1.2"><link rel="icon" type="image/png" sizes="16x16" href="/blog/icos/favicon-32x32.ico?v=7.1.2"><link rel="mask-icon" href="/blog/images/logo.svg?v=7.1.2" color="#222"><script id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/blog/",scheme:"Pisces",version:"7.1.2",sidebar:{position:"left",display:"post",offset:12,onmobile:!1,dimmer:!1},back2top:!0,back2top_sidebar:!1,fancybox:!0,fastclick:!0,lazyload:!0,tabs:!0,motion:{enable:!0,async:!0,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},algolia:{applicationID:"",apiKey:"",indexName:"",hits:{per_page:10},labels:{input_placeholder:"Search for Posts",hits_empty:"We didn't find any results for the search: ${query}",hits_stats:"${hits} results found in ${time} ms"}}}</script><meta name="description" content="这是很久之前的我们业务组内部的一个分享，一直没有更新发布了。乘此再复习一遍vue-router 是什么首先我们需要知道vue-router是什么，它是干什么的？这里的路由并不是指我们平时所说的硬件路由器，这里的路由就是SPA（单页应用）的路径管理器。 换句话说，vue-router就是WebApp的链接路径管理系统。路由是根据不同的url地址展示不同的内容或者页面。前端路由就是把不同路由对应不同的"><meta name="keywords" content="vue,vue-router"><meta property="og:type" content="article"><meta property="og:title" content="vue-router实现原理分享"><meta property="og:url" content="https://liliuzhu.gitee.io/blog/2020/11/vue-router-principle-sharing.html"><meta property="og:site_name" content="Luther Li&#39;s Blog"><meta property="og:description" content="这是很久之前的我们业务组内部的一个分享，一直没有更新发布了。乘此再复习一遍vue-router 是什么首先我们需要知道vue-router是什么，它是干什么的？这里的路由并不是指我们平时所说的硬件路由器，这里的路由就是SPA（单页应用）的路径管理器。 换句话说，vue-router就是WebApp的链接路径管理系统。路由是根据不同的url地址展示不同的内容或者页面。前端路由就是把不同路由对应不同的"><meta property="og:locale" content="zh-CN"><meta property="og:image" content="https://liliuzhu.github.io/CDN//images/blog/2020/vue-router.png"><meta property="og:updated_time" content="2020-11-03T10:51:30.548Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="vue-router实现原理分享"><meta name="twitter:description" content="这是很久之前的我们业务组内部的一个分享，一直没有更新发布了。乘此再复习一遍vue-router 是什么首先我们需要知道vue-router是什么，它是干什么的？这里的路由并不是指我们平时所说的硬件路由器，这里的路由就是SPA（单页应用）的路径管理器。 换句话说，vue-router就是WebApp的链接路径管理系统。路由是根据不同的url地址展示不同的内容或者页面。前端路由就是把不同路由对应不同的"><meta name="twitter:image" content="https://liliuzhu.github.io/CDN//images/blog/2020/vue-router.png"><link rel="alternate" href="/blog/atom.xml" title="Luther Li's Blog" type="application/atom+xml"><link rel="canonical" href="https://liliuzhu.gitee.io/blog/2020/11/vue-router-principle-sharing"><script id="page.configurations">CONFIG.page={sidebar:""}</script><title>vue-router实现原理分享 | Luther Li's Blog</title><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?2a46638d9d90d8e9e8bd06f78263ffa4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><noscript><style>.sidebar-inner,.use-motion .brand,.use-motion .collection-title,.use-motion .comments,.use-motion .menu-item,.use-motion .motion-element,.use-motion .pagination,.use-motion .post-block,.use-motion .post-body,.use-motion .post-header{opacity:initial}.use-motion .logo,.use-motion .site-subtitle,.use-motion .site-title{opacity:initial;top:initial}.use-motion .logo-line-before i{left:initial}.use-motion .logo-line-after i{right:initial}</style></noscript></head><body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN"><div class="container sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-wrapper"><div class="site-meta"><div class="custom-logo-site-title"><a href="/blog/" class="brand" rel="start"><span class="logo-line-before"><i></i></span> <span class="site-title">Luther Li's Blog</span> <span class="logo-line-after"><i></i></span></a></div><p class="site-subtitle">越努力，越精彩</p></div><div class="site-nav-toggle"><button aria-label="切换导航栏"><span class="btn-bar"></span> <span class="btn-bar"></span> <span class="btn-bar"></span></button></div></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-home"><a href="/blog/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i><br>首页</a></li><li class="menu-item menu-item-about"><a href="/blog/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i><br>关于</a></li><li class="menu-item menu-item-tags"><a href="/blog/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br>标签</a></li><li class="menu-item menu-item-categories"><a href="/blog/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br>分类</a></li><li class="menu-item menu-item-archives"><a href="/blog/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br>归档</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br>搜索</a></li></ul><div class="site-search"><div class="popup search-popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i> </span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"><input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div></header><a href="https://github.com/liliuzhu" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewbox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin:130px 106px" class="octo-arm"/><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"/></svg></a><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><div class="post-block"><link itemprop="mainEntityOfPage" href="https://liliuzhu.gitee.io/blog/blog/2020/11/vue-router-principle-sharing.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="Luther Li"><meta itemprop="description" content="本人前端菜鸟一枚，踏在前端这条不归路上<br/>渐行渐远..."><meta itemprop="image" content="/blog/uploads/avatar.jpg"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Luther Li's Blog"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">vue-router实现原理分享</h1><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间：2020-11-03 18:25:27 / 修改时间：10:51:30" itemprop="dateCreated datePublished" datetime="2020-11-03T18:25:27Z">2020-11-03</time> </span><span class="post-category"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-folder-o"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/原创/" itemprop="url" rel="index"><span itemprop="name">原创</span></a></span> ， <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/前端/" itemprop="url" rel="index"><span itemprop="name">前端</span></a></span> ， <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/blog/categories/vue/" itemprop="url" rel="index"><span itemprop="name">vue</span></a></span> </span><span class="post-comments-count"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-comment-o"></i> </span><span class="post-meta-item-text">评论数：</span> <a href="/blog/2020/11/vue-router-principle-sharing.html#comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/blog/2020/11/vue-router-principle-sharing.html" itemprop="commentCount"></span> </a></span><span id="/blog/2020/11/vue-router-principle-sharing.html" class="leancloud_visitors" data-flag-title="vue-router实现原理分享"><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="post-meta-item-text">阅读次数：</span> <span class="leancloud-visitors-count"></span></span><div class="post-symbolscount"><span class="post-meta-item-icon"><i class="fa fa-file-word-o"></i> </span><span class="post-meta-item-text">本文字数：</span> <span title="本文字数">10k</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-clock-o"></i> </span><span class="post-meta-item-text">阅读时长 &asymp;</span> <span title="阅读时长">17 分钟</span></div></div></header><div class="post-body han-init-context" itemprop="articleBody"><div class="post-gallery" itemscope itemtype="http://schema.org/ImageGallery"><div class="post-gallery-row"><img src="//liliuzhu.github.io/CDN//images/blog/2020/vue-router.png" itemprop="contentUrl"></div></div><p>这是很久之前的我们业务组内部的一个分享，一直没有更新发布了。乘此再复习一遍</p><h2 id="vue-router-是什么"><a href="#vue-router-是什么" class="headerlink" title="vue-router  是什么"></a>vue-router 是什么</h2><p>首先我们需要知道vue-router是什么，它是干什么的？</p><p>这里的路由并不是指我们平时所说的硬件路由器，这里的路由就是SPA（单页应用）的路径管理器。 换句话说，vue-router就是WebApp的链接路径管理系统。</p><p>路由是根据不同的url地址展示不同的内容或者页面。前端路由就是把不同路由对应不同的内容或者页面的任务交给前端来做，之前是通过服务端根据url的不同返回不同的页面实现的。</p><p>vue-router是Vue.js官方的路由插件，它和vue.js是深度集成的，适合用于构建单页面应用。<br><a id="more"></a></p><p>那与传统的页面跳转有什么区别呢？</p><ol><li>vue的单页面应用是基于路由和组件的，路由用于设定访问路径，并将路径和组件映射起来。</li><li>传统的页面应用，是用一些超链接来实现页面切换和跳转的。</li></ol><p>在vue-router单页面应用中，则是路径之间的切换，也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。</p><h2 id="vue-router-产生的时代背景"><a href="#vue-router-产生的时代背景" class="headerlink" title="vue-router 产生的时代背景"></a>vue-router 产生的时代背景</h2><p>随着 ajax 的流行，异步数据请求交互运行在不刷新浏览器的情况下进行。而异步交互体验的更高级版本就是 SPA —— 单页应用。单页应用不仅仅是在页面交互是无刷新的，连页面跳转都是无刷新的，为了实现单页应用，所以就有了前端路由。</p><h2 id="为什么要使用vue-router"><a href="#为什么要使用vue-router" class="headerlink" title="为什么要使用vue-router"></a>为什么要使用vue-router</h2><p>至于我们为啥不能用a标签，这是因为用Vue做的都是单页面应用（当你的项目准备打包时，运行 npm run build时，就会生成dist文件夹，这里面只有静态资源和一个index.html页面，在没有后端服务的支持下，浏览器无法找到对应的url路径的），所以你写的标签是不起作用的，你必须使用vue-router来进行管理。</p><p>Vue Router 包含的功能有：</p><ul><li>嵌套的路由/视图表</li><li>模块化的、基于组件的路由配置</li><li>路由参数、查询、通配符</li><li>基于 Vue.js 过渡系统的视图过渡效果</li><li>细粒度的导航控制</li><li>带有自动激活的 CSS class 的链接</li><li>HTML5 历史模式或 hash 模式，在 IE9 中自动降级</li><li>自定义的滚动条行为</li></ul><h2 id="如何使用vue-router"><a href="#如何使用vue-router" class="headerlink" title="如何使用vue-router"></a>如何使用vue-router</h2><p>不做讲解，都用过无数遍了。看vue-router文档 <a href="https://router.vuejs.org/zh/" target="_blank" rel="noopener">https://router.vuejs.org/zh/</a></p><h2 id="vue-router实现原理"><a href="#vue-router实现原理" class="headerlink" title="vue-router实现原理"></a>vue-router实现原理</h2><p>SPA(single page application):单一页面应用程序，只有一个完整的页面；它在加载页面时，不会加载整个页面，而是只更新某个指定的容器中内容。</p><p>vue-router 使用 <a href="https://github.com/pillarjs/path-to-regexp" target="_blank" rel="noopener">path-to-regexp</a> 作为路径匹配引擎，用来匹配path和params</p><p>单页面应用(SPA)的核心之一是:</p><ol><li>更新视图而不重新请求页面</li><li>vue-router在实现单页面前端路由时，提供了三种方式：Hash模式和History模式；根据mode参数以及运行环境决定采用哪一种方式。</li></ol><h3 id="1-hash-模式"><a href="#1-hash-模式" class="headerlink" title="1. hash 模式"></a>1. hash 模式</h3><p>vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL，于是当 URL 改变时，页面不会重新加载。hash（#）是URL 的锚点，代表的是网页中的一个位置，单单改变#后的部分，浏览器只会滚动到相应位置，不会重新加载网页，也就是说hash 出现在 URL 中，但不会被包含在 http 请求中，对后端完全没有影响，因此改变 hash 不会重新加载页面；同时每一次改变#后的部分，都会在浏览器的访问历史中增加一个记录，使用”后退”按钮，就可以回到上一个位置；所以说Hash模式通过锚点值的改变，根据不同的值，渲染指定DOM位置的不同数据。hash 模式的原理是 onhashchange 事件(监测hash值变化)，可以在 window 对象上监听这个事件。</p><p>在 2014 年之前，大家是通过 hash 来实现路由，url hash 就是类似于：</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">http:</span>//www.renrenche.com/<span class="meta">#/page1</span></span><br></pre></td></tr></table></figure><p>后来，因HTML5的发布，又出现了一个onpopstate事件，其可以代替onhashchange使用</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(</span><br><span class="line">  supportsPushState ? <span class="string">'popstate'</span> : <span class="string">'hashchange'</span>,</span><br><span class="line">  () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> current = <span class="keyword">this</span>.current</span><br><span class="line">    <span class="keyword">if</span> (!ensureSlash()) &#123;</span><br><span class="line">      <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.transitionTo(getHash(), route =&gt; &#123;</span><br><span class="line">      <span class="keyword">if</span> (supportsScroll) &#123;</span><br><span class="line">        handleScroll(<span class="keyword">this</span>.router, route, current, <span class="literal">true</span>)</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!supportsPushState) &#123;</span><br><span class="line">        replaceHash(route.fullPath)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="2-history-模式"><a href="#2-history-模式" class="headerlink" title="2. history 模式"></a>2. history 模式</h3><p>由于hash模式会在url中自带#，如果不想要很丑的hash，我们可以用vue-router的 history 模式。</p><p>14年后，因为HTML5标准发布。html5 history interface 中新增的了 pushState() 和 replaceState()方法这两个方法应用于浏览器记录栈，在当前已有的 back、forward、go基础之上，它们提供了对历史记录修改的功能。只是当它们执行修改时，虽然改变了当前的URL，但浏览器不会立即向后端发送请求。同时还有popstate事件。通过这些就能用另一种方式来实现前端路由了，但原理都是跟hash实现相同的。</p><p>当你使用 history 模式时，URL就像正常的url，单页路由的url就不会多出一个#，例如 <a href="http://shanyishanmei.com/user/id" target="_blank" rel="noopener">http://shanyishanmei.com/user/id</a> 变得更加美观！但因为没有#号，所以当用户刷新页面之类的操作时，浏览器还是会给服务器发送请求。为了避免出现这种情况，所以这个实现需要服务器的支持，需要把所有路由都重定向到根页面。因为我们的应用是个单页客户端应用，如果后台没有正确的配置，当用户在浏览器直接访问<a href="http://shanyishanmei.com/user/id2就会返回404，这就不对了。所以呢，你要在服务端增加一个覆盖所有情况的候选资源：如果" target="_blank" rel="noopener">http://shanyishanmei.com/user/id2就会返回404，这就不对了。所以呢，你要在服务端增加一个覆盖所有情况的候选资源：如果</a> URL 匹配不到任何静态资源，则应该返回同一个index.html页面，这个页面就是你 app 依赖的页面。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'popstate'</span>, e =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> current = <span class="keyword">this</span>.current</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Avoiding first `popstate` event dispatched in some browsers but first</span></span><br><span class="line">  <span class="comment">// history route not updated since async guard at the same time.</span></span><br><span class="line">  <span class="comment">// 避免在某些浏览器中触发第一个“popstate”事件，但同时由于异步保护而未更新第一个历史路由。</span></span><br><span class="line">  <span class="keyword">const</span> location = getLocation(<span class="keyword">this</span>.base)</span><br><span class="line">  <span class="keyword">if</span> (<span class="keyword">this</span>.current === START &amp;&amp; location === initLocation) &#123;</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">this</span>.transitionTo(location, route =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (supportsScroll) &#123;</span><br><span class="line">      handleScroll(router, route, current, <span class="literal">true</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="3-abstract-模式"><a href="#3-abstract-模式" class="headerlink" title="3. abstract 模式"></a>3. abstract 模式</h3><p>通过一个数组和一个数字变量来模拟浏览器的history的。支持所有 JavaScript 运行环境，如 Node.js 服务器端。如果发现没有浏览器的 API，路由会自动强制进入这个模式</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">constructor</span> (router: Router, base: ?string) &#123;</span><br><span class="line">    <span class="keyword">super</span>(router, base)</span><br><span class="line">    <span class="keyword">this</span>.stack = []</span><br><span class="line">    <span class="keyword">this</span>.index = <span class="number">-1</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  push (location: RawLocation, onComplete?: <span class="built_in">Function</span>, onAbort?: <span class="built_in">Function</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.transitionTo(</span><br><span class="line">      location,</span><br><span class="line">      route =&gt; &#123;</span><br><span class="line">        <span class="keyword">this</span>.stack = <span class="keyword">this</span>.stack.slice(<span class="number">0</span>, <span class="keyword">this</span>.index + <span class="number">1</span>).concat(route)</span><br><span class="line">        <span class="keyword">this</span>.index++</span><br><span class="line">        onComplete &amp;&amp; onComplete(route)</span><br><span class="line">      &#125;,</span><br><span class="line">      onAbort</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  replace (location: RawLocation, onComplete?: <span class="built_in">Function</span>, onAbort?: <span class="built_in">Function</span>) &#123;</span><br><span class="line">    <span class="keyword">this</span>.transitionTo(</span><br><span class="line">      location,</span><br><span class="line">      route =&gt; &#123;</span><br><span class="line">        <span class="keyword">this</span>.stack = <span class="keyword">this</span>.stack.slice(<span class="number">0</span>, <span class="keyword">this</span>.index).concat(route)</span><br><span class="line">        onComplete &amp;&amp; onComplete(route)</span><br><span class="line">      &#125;,</span><br><span class="line">      onAbort</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  go (n: number) &#123;</span><br><span class="line">    <span class="keyword">const</span> targetIndex = <span class="keyword">this</span>.index + n</span><br><span class="line">    <span class="keyword">if</span> (targetIndex &lt; <span class="number">0</span> || targetIndex &gt;= <span class="keyword">this</span>.stack.length) &#123;</span><br><span class="line">      <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">const</span> route = <span class="keyword">this</span>.stack[targetIndex]</span><br><span class="line">    <span class="keyword">this</span>.confirmTransition(</span><br><span class="line">      route,</span><br><span class="line">      () =&gt; &#123;</span><br><span class="line">        <span class="keyword">this</span>.index = targetIndex</span><br><span class="line">        <span class="keyword">this</span>.updateRoute(route)</span><br><span class="line">      &#125;,</span><br><span class="line">      err =&gt; &#123;</span><br><span class="line">        <span class="keyword">if</span> (isExtendedError(NavigationDuplicated, err)) &#123;</span><br><span class="line">          <span class="keyword">this</span>.index = targetIndex</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><h3 id="popstate介绍"><a href="#popstate介绍" class="headerlink" title="popstate介绍"></a>popstate介绍</h3><p>当活动历史记录条目更改时，将触发popstate事件。如果被激活的历史记录条目是通过对history.pushState（）的调用创建的，或者受到对history.replaceState（）的调用的影响，popstate事件的state属性包含历史条目的状态对象的副本。</p><p>需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时，才会触发该事件，如用户点击浏览器的回退按钮（或者在Javascript代码中调用history.back()）</p><p>不同的浏览器在加载页面时处理popstate事件的形式存在差异。页面加载时Chrome和Safari通常会触发(emit )popstate事件，但Firefox则不会</p><p>根据popstate介绍以及从vue-router源码，当我们手动改变url的hash的时候或者window.location.hash = ‘xxx’,history.go(-1), history.back(),history.forward() 的时候才会触发onpopstate或者onhashchange事件，进而运行回调transitionTo方法。而window.history.replaceState和window.history.pushState，仅会改变历史记录条目，无法触发onpopstate，所以目前hash和history模式下，$router的push,replace方法都是直接调用transitionTo方法来更新视图而后再用history.replaceState和history.pushState改变；历史记录和url</p><h2 id="vue-router源码分析"><a href="#vue-router源码分析" class="headerlink" title="vue-router源码分析"></a>vue-router源码分析</h2><h3 id="install-js-分析"><a href="#install-js-分析" class="headerlink" title="install.js 分析"></a>install.js 分析</h3><ol><li>首先会对重复安装进行过滤</li><li>全局混入beforeCreate和destroyed 生命钩子，为每个Vue实例设置 _routerRoot属性，并为跟实例设置_router属性</li><li>调用Vue中定义的defineReactive对_route进行劫持，其实是执行的依赖收集的过程，执行_route的get就会对当前的组件进行依赖收集，如果对_route进行重新赋值触发setter就会使收集的组件重新渲染，这里也是路由重新渲染的核心所在</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">Vue.mixin(&#123;</span><br><span class="line">  beforeCreate () &#123;</span><br><span class="line">    <span class="keyword">if</span> (isDef(<span class="keyword">this</span>.$options.router)) &#123; <span class="comment">// 设置根路由-根组件实例</span></span><br><span class="line">      <span class="keyword">this</span>._routerRoot = <span class="keyword">this</span></span><br><span class="line">      <span class="keyword">this</span>._router = <span class="keyword">this</span>.$options.router</span><br><span class="line">      <span class="keyword">this</span>._router.init(<span class="keyword">this</span>)</span><br><span class="line">      <span class="comment">// 定义响应式的 _route 对象</span></span><br><span class="line">      Vue.util.defineReactive(<span class="keyword">this</span>, <span class="string">'_route'</span>, <span class="keyword">this</span>._router.history.current)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123; <span class="comment">// 非根组件设置</span></span><br><span class="line">      <span class="keyword">this</span>._routerRoot = (<span class="keyword">this</span>.$parent &amp;&amp; <span class="keyword">this</span>.$parent._routerRoot) || <span class="keyword">this</span></span><br><span class="line">    &#125;</span><br><span class="line">    registerInstance(<span class="keyword">this</span>, <span class="keyword">this</span>)</span><br><span class="line">  &#125;,</span><br><span class="line">  destroyed () &#123;</span><br><span class="line">    registerInstance(<span class="keyword">this</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><ol start="4"><li>为Vue原型对象定义$router和$route属性，并对两个属性进行了劫持，使我们可以直接通过Vue对象实例访问到</li><li>全局注册了Routerview和RouterLink两个组件，所以我们才可以在任何地方使用这两个组件，这两个组件的内容我们稍后分析</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.defineProperty(Vue.prototype, <span class="string">'$router'</span>, &#123;</span><br><span class="line">   <span class="keyword">get</span> () &#123; <span class="keyword">return</span> <span class="keyword">this</span>._routerRoot._router &#125;</span><br><span class="line"> &#125;)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(Vue.prototype, <span class="string">'$route'</span>, &#123;</span><br><span class="line">   <span class="keyword">get</span> () &#123; <span class="keyword">return</span> <span class="keyword">this</span>._routerRoot._route &#125;</span><br><span class="line"> &#125;)</span><br></pre></td></tr></table></figure><h3 id="RouterView-和-RouterLink-组件分析"><a href="#RouterView-和-RouterLink-组件分析" class="headerlink" title="RouterView 和 RouterLink 组件分析"></a>RouterView 和 RouterLink 组件分析</h3><h4 id="RouterView"><a href="#RouterView" class="headerlink" title="RouterView"></a>RouterView</h4><p><router-view>是无状态(没有 data ) 和无实例 (没有 this 上下文)的函数式组件。用一个简单的render函数返回虚拟节点使他们更容易渲染。</router-view></p><p><router-view>渲染的组件还可以内嵌<router-view>，根据嵌套路径，渲染嵌套组件。</router-view></router-view></p><p><router-view>类似一个占位插槽，并不会渲染本身DOM模板，而是根据自身所在<router-view>嵌套层级以及<router-view>的render函数的第二个参数作为参数，匹配当前路由($route === $router.history.current)的嵌套层级matched，然后再用已匹配的matched的components中找到和RouterView name相同的组件。并渲染对应匹配到的组件，否则渲染空组件</router-view></router-view></router-view></p><h4 id="RouterLink"><a href="#RouterLink" class="headerlink" title="RouterLink"></a>RouterLink</h4><p>相比<router-view>，<router-link>是一个普通的非抽象组件，通过router的resolve方法解析自身的to属性参数，并调用$router.push或者replace方法进行路由跳转。</router-link></router-view></p><h3 id="index-js-入口分析"><a href="#index-js-入口分析" class="headerlink" title="index.js 入口分析"></a>index.js 入口分析</h3><ol><li>声明Router类，以及原型方法，实例属性等<br>例如一下常用的方法</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">push，replace，go，back，forward, addRoutes等</span><br></pre></td></tr></table></figure><p>全局路由守卫<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">beforeEach，beforeResolve，afterEach</span><br></pre></td></tr></table></figure><p></p><ol start="2"><li>路由模式的判断,以及不同模式采用不同的策略</li><li>等</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mode = options.mode || <span class="string">'hash'</span></span><br><span class="line"><span class="comment">// 通过 supportsPushState 判断浏览器是否支持'history'模式</span></span><br><span class="line"><span class="comment">// 如果设置的是'history'但是如果浏览器不支持的话，'history'模式会退回到'hash'模式</span></span><br><span class="line"><span class="comment">// fallback 是当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。</span></span><br><span class="line"><span class="keyword">this</span>.fallback = mode === <span class="string">'history'</span> &amp;&amp; !supportsPushState &amp;&amp; options.fallback !== <span class="literal">false</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.fallback) &#123;</span><br><span class="line">  mode = <span class="string">'hash'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 不在浏览器内部的话，就会变成'abstract'模式</span></span><br><span class="line"><span class="keyword">if</span> (!inBrowser) &#123;</span><br><span class="line">  mode = <span class="string">'abstract'</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">this</span>.mode = mode</span><br><span class="line"><span class="comment">// 根据不同模式选择实例化对应的 History 类</span></span><br><span class="line"><span class="keyword">switch</span> (mode) &#123;</span><br><span class="line">  <span class="keyword">case</span> <span class="string">'history'</span>:</span><br><span class="line">    <span class="keyword">this</span>.history = <span class="keyword">new</span> HTML5History(<span class="keyword">this</span>, options.base)</span><br><span class="line">    <span class="keyword">break</span></span><br><span class="line">  <span class="keyword">case</span> <span class="string">'hash'</span>:</span><br><span class="line">    <span class="keyword">this</span>.history = <span class="keyword">new</span> HashHistory(<span class="keyword">this</span>, options.base, <span class="keyword">this</span>.fallback)</span><br><span class="line">    <span class="keyword">break</span></span><br><span class="line">  <span class="keyword">case</span> <span class="string">'abstract'</span>:</span><br><span class="line">    <span class="keyword">this</span>.history = <span class="keyword">new</span> AbstractHistory(<span class="keyword">this</span>, options.base)</span><br><span class="line">    <span class="keyword">break</span></span><br><span class="line">  <span class="keyword">default</span>:</span><br><span class="line">    <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) &#123;</span><br><span class="line">      assert(<span class="literal">false</span>, <span class="string">`invalid mode: <span class="subst">$&#123;mode&#125;</span>`</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="总体流分析"><a href="#总体流分析" class="headerlink" title="总体流分析"></a>总体流分析</h3><ol><li>安装 vue-router 插件（参考 install.js分析）</li><li>new Router 实例</li><li>根实例创建之前，执行init方法，初始化路由</li><li>执行transitionTo方法，同时hash模式下对浏览器hashChange事件进行了监听，执行history.listen方法，将对_route重新赋值的函数赋给History实例的callback，当路由改变时对_route进行重新赋值从而触发组件更新</li><li>transitionTo方法根据传入的路径从我们定义的所有路由中匹配到对应路由，然后执行confirmTransition</li><li>confirmTransition首先会有重复路由的判断，如果进入相同的路由，直接调用abort回调函数，函数退出，不会执行后面的各组件的钩子函数，这也是为什么我们重复进入相同路由不会触发组建的重新渲染也不会触发路由的各种钩子函数，<br>如果判断不是相同路由，就会执行各组件的钩子函数(高阶函数太多，没看懂)。导航守卫执行顺序</li></ol><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span>导航被触发。</span><br><span class="line"><span class="number">2.</span>在失活的组件里调用离开守卫。</span><br><span class="line"><span class="number">3.</span>调用全局的 beforeEach 守卫。</span><br><span class="line"><span class="number">4.</span>在重用的组件里调用 beforeRouteUpdate 守卫 (<span class="number">2.2</span>+)。</span><br><span class="line"><span class="number">5.</span>在路由配置里调用 beforeEnter。</span><br><span class="line"><span class="number">6.</span>解析异步路由组件。</span><br><span class="line"><span class="number">7.</span>在被激活的组件里调用 beforeRouteEnter。</span><br><span class="line"><span class="number">8.</span>调用全局的 beforeResolve 守卫 (<span class="number">2.5</span>+)。</span><br><span class="line"><span class="number">9.</span>导航被确认。</span><br><span class="line"><span class="number">10.</span>调用全局的 afterEach 钩子。</span><br><span class="line"><span class="number">11.</span>触发 DOM 更新。</span><br><span class="line"><span class="number">12.</span>用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。</span><br></pre></td></tr></table></figure><ol start="7"><li>按顺序执行好导航守卫后，就会执行传入的成功的回调函数,从而对_route进行赋值，触发setter，从而使组件重新渲染</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// confirmTransition 的成功回调</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.updateRoute(route)</span><br><span class="line">    onComplete &amp;&amp; onComplete(route)</span><br><span class="line">    <span class="keyword">this</span>.ensureURL()</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// fire ready cbs once</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>.ready) &#123;</span><br><span class="line">      <span class="keyword">this</span>.ready = <span class="literal">true</span></span><br><span class="line">      <span class="keyword">this</span>.readyCbs.forEach(<span class="function"><span class="params">cb</span> =&gt;</span> &#123;</span><br><span class="line">        cb(route)</span><br><span class="line">      &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新 router</span></span><br><span class="line">updateRoute (route: Route) &#123;</span><br><span class="line">    <span class="keyword">const</span> prev = <span class="keyword">this</span>.current</span><br><span class="line">    <span class="keyword">this</span>.current = route</span><br><span class="line">    <span class="keyword">this</span>.cb &amp;&amp; <span class="keyword">this</span>.cb(route)</span><br><span class="line">    <span class="keyword">this</span>.router.afterHooks.forEach(<span class="function"><span class="params">hook</span> =&gt;</span> &#123; <span class="comment">// 触发全局afterEach守卫</span></span><br><span class="line">      hook &amp;&amp; hook(route, prev)</span><br><span class="line">    &#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="8"><li>this.$router.push， this.$router.replace， router-link，都会执行transitionTo，history.go()等，又会触发popstate或者hashchange而后在执行transitionTo</li><li>里面细节很多，就不一一说了</li></ol><p>总体流程图<br><img src="https://liliuzhu.github.io/CDN//images/blog/2020/vue-router_flow_chart.jpg" alt="image"></p><h2 id="vue-router的优缺点"><a href="#vue-router的优缺点" class="headerlink" title="vue-router的优缺点"></a>vue-router的优缺点</h2><p>优点：</p><ol><li>良好的交互体验,用户不需要刷新页面,页面显示流畅；</li><li>良好的前后端工作分离模式,减轻服务器压力,</li><li>完全的前端组件化，便于修改和调整</li></ol><p>缺点：</p><ol><li>首次加载大量资源，要在一个页面上为用户提供产品的所有功能，在这个页面加载的时候，首先要加载大量的静态资源，这个加载时间相对比较长；</li><li>不利于 SEO，单页页面，数据在前端渲染，就意味着没有SEO，或者需要使用变通的方案。</li></ol><h2 id="个人对vue-router-目前遇到的问题"><a href="#个人对vue-router-目前遇到的问题" class="headerlink" title="个人对vue-router 目前遇到的问题"></a>个人对vue-router 目前遇到的问题</h2><p>confirmTransition 这个高阶函数一层套一层，绕不过来具体看源码’src/history/base.js’</p><p>异步组件的加载原理</p><blockquote><p>本文首发于个人技术博客 <a href="https://liliuzhu.gitee.io/blog">https://liliuzhu.gitee.io/blog</a></p></blockquote></div><div><div id="reward-container"><div>坚持技术分享，您的支持将鼓励我继续努力！</div><button id="reward-button" disable="enable" onclick="var qr = document.getElementById(&quot;qr&quot;); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">打赏</button><div id="qr" style="display:none"><div style="display:inline-block"><img src="/blog/uploads/wechatpay.jpg" alt="Luther Li 微信支付"><p>微信支付</p></div><div style="display:inline-block"><img src="/blog/uploads/alipay.jpg" alt="Luther Li 支付宝"><p>支付宝</p></div></div></div></div><div><ul class="post-copyright"><li class="post-copyright-author"><strong>本文作者： </strong>Luther Li</li><li class="post-copyright-link"><strong>本文链接：</strong> <a href="https://liliuzhu.gitee.io/blog/2020/11/vue-router-principle-sharing.html" title="vue-router实现原理分享">https://liliuzhu.gitee.io/blog/2020/11/vue-router-principle-sharing.html</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fa fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！</li></ul></div><footer class="post-footer"><div class="post-tags"><a href="/blog/tags/vue/" rel="tag"># vue</a> <a href="/blog/tags/vue-router/" rel="tag"># vue-router</a></div><div class="post-widgets"><div class="social_share"><div><div class="bdsharebuttonbox"><a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a> <a href="#" class="bds_douban" data-cmd="douban" title="分享到豆瓣网"></a> <a href="#" class="bds_sqq" data-cmd="sqq" title="分享到QQ好友"></a> <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间"></a> <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信"></a> <a href="#" class="bds_tieba" data-cmd="tieba" title="分享到百度贴吧"></a> <a href="#" class="bds_twi" data-cmd="twi" title="分享到Twitter"></a> <a href="#" class="bds_fbook" data-cmd="fbook" title="分享到Facebook"></a> <a href="#" class="bds_more" data-cmd="more"></a> <a class="bds_count" data-cmd="count"></a></div><script>window._bd_share_config={common:{bdText:"",bdMini:"2",bdMiniList:!1,bdPic:""},share:{bdSize:"16",bdStyle:"0"},image:{viewList:["tsina","douban","sqq","qzone","weixin","twi","fbook"],viewText:"分享到：",viewSize:"16"}}</script><script>with(document)(0)[(getElementsByTagName("head")[0]||body).appendChild(createElement("script")).src="/static/api/js/share.js?cdnversion="+~(-new Date/36e5)]</script></div></div></div><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/blog/2019/10/vue_use_babel-polyfill.html" rel="next" title="vue中babel-polyfill的使用方法"><i class="fa fa-chevron-left"></i> vue中babel-polyfill的使用方法</a></div><span class="post-nav-divider"></span><div class="post-nav-prev post-nav-item"><a href="/blog/2020/11/JS-design-patterns-share-1.html" rel="prev" title="JS设计模式之代理模式&发布订阅模式的拓展参考">JS设计模式之代理模式&发布订阅模式的拓展参考 <i class="fa fa-chevron-right"></i></a></div></div></footer></div></article></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span> <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span> <span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">文章目录</li><li class="sidebar-nav-overview" data-target="site-overview-wrap">站点概览</li></ul><div class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="site-author-image" itemprop="image" src="/blog/uploads/avatar.jpg" alt="Luther Li"><p class="site-author-name" itemprop="name">Luther Li</p><div class="site-description motion-element" itemprop="description">本人前端菜鸟一枚，踏在前端这条不归路上<br>渐行渐远...</div></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"><a href="/blog/archives/"><span class="site-state-item-count">8</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"><a href="/blog/categories/"><span class="site-state-item-count">8</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"><a href="/blog/tags/"><span class="site-state-item-count">10</span> <span class="site-state-item-name">标签</span></a></div></nav><div class="feed-link motion-element"><a href="/blog/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/liliuzhu" title="GitHub &rarr; https://github.com/liliuzhu" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a> </span><span class="links-of-author-item"><a href="mailto:liliuzhu1992@163.com" title="E-Mail &rarr; mailto:liliuzhu1992@163.com" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a> </span><span class="links-of-author-item"><a href="http://blog.csdn.net/yuhouxinya" title="CSDN &rarr; http://blog.csdn.net/yuhouxinya" rel="noopener" target="_blank"><i class="fa fa-fw fa-book"></i>CSDN</a> </span><span class="links-of-author-item"><a href="https://juejin.im/user/58d69e3c44d904006870487f" title="掘金 &rarr; https://juejin.im/user/58d69e3c44d904006870487f" rel="noopener" target="_blank"><i class="fa fa-fw fa-book"></i>掘金</a> </span><span class="links-of-author-item"><a href="https://liliuzhu.gitee.io" title="个人主页 &rarr; https://liliuzhu.gitee.io"><i class="fa fa-fw fa-leaf"></i>个人主页</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-inline"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-link"></i> 推荐阅读</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"><a href="http://www.uisdc.com/" title="http://www.uisdc.com/" rel="noopener" target="_blank">优设</a></li><li class="links-of-blogroll-item"><a href="http://www.zhangxinxu.com/" title="http://www.zhangxinxu.com/" rel="noopener" target="_blank">张鑫旭</a></li><li class="links-of-blogroll-item"><a href="http://www.alloyteam.com/nav/" title="http://www.alloyteam.com/nav/" rel="noopener" target="_blank">Web前端导航</a></li><li class="links-of-blogroll-item"><a href="http://www.36zhen.com/t?id=3448" title="http://www.36zhen.com/t?id=3448" rel="noopener" target="_blank">前端书籍资料</a></li><li class="links-of-blogroll-item"><a href="http://ife.baidu.com/" title="http://ife.baidu.com/" rel="noopener" target="_blank">百度前端技术学院</a></li><li class="links-of-blogroll-item"><a href="http://wf.uisdc.com/cn/" title="http://wf.uisdc.com/cn/" rel="noopener" target="_blank">google前端开发基础</a></li></ul></div></div></div><div class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#vue-router-是什么"><span class="nav-number">1.</span> <span class="nav-text">vue-router 是什么</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#vue-router-产生的时代背景"><span class="nav-number">2.</span> <span class="nav-text">vue-router 产生的时代背景</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#为什么要使用vue-router"><span class="nav-number">3.</span> <span class="nav-text">为什么要使用vue-router</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#如何使用vue-router"><span class="nav-number">4.</span> <span class="nav-text">如何使用vue-router</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#vue-router实现原理"><span class="nav-number">5.</span> <span class="nav-text">vue-router实现原理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-hash-模式"><span class="nav-number">5.1.</span> <span class="nav-text">1. hash 模式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-history-模式"><span class="nav-number">5.2.</span> <span class="nav-text">2. history 模式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-abstract-模式"><span class="nav-number">5.3.</span> <span class="nav-text">3. abstract 模式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#popstate介绍"><span class="nav-number">5.4.</span> <span class="nav-text">popstate介绍</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#vue-router源码分析"><span class="nav-number">6.</span> <span class="nav-text">vue-router源码分析</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#install-js-分析"><span class="nav-number">6.1.</span> <span class="nav-text">install.js 分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#RouterView-和-RouterLink-组件分析"><span class="nav-number">6.2.</span> <span class="nav-text">RouterView 和 RouterLink 组件分析</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#RouterView"><span class="nav-number">6.2.1.</span> <span class="nav-text">RouterView</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#RouterLink"><span class="nav-number">6.2.2.</span> <span class="nav-text">RouterLink</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#index-js-入口分析"><span class="nav-number">6.3.</span> <span class="nav-text">index.js 入口分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#总体流分析"><span class="nav-number">6.4.</span> <span class="nav-text">总体流分析</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#vue-router的优缺点"><span class="nav-number">7.</span> <span class="nav-text">vue-router的优缺点</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#个人对vue-router-目前遇到的问题"><span class="nav-number">8.</span> <span class="nav-text">个人对vue-router 目前遇到的问题</span></a></li></ol></div></div></div></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright">&copy; <span itemprop="copyrightYear">2020</span> <span class="with-love" id="animate"><i class="fa fa-user"></i> </span><span class="author" itemprop="copyrightHolder">Luther Li</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-area-chart"></i> </span><span class="post-meta-item-text">站点总字数：</span> <span title="站点总字数">57k</span></div><div class="busuanzi-count"><script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="post-meta-item-icon"><i class="fa fa-user"></i> </span><span class="site-uv" title="总访客量"><span class="busuanzi-value" id="busuanzi_value_site_uv"></span> </span><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="site-pv" title="总访问量"><span class="busuanzi-value" id="busuanzi_value_site_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i> <span id="scrollpercent"><span>0</span>%</span></div></div><script>"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script color="0,0,255" opacity="0.5" zindex="10" count="99" src="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script><script src="/blog/lib/jquery/index.js?v=2.1.3"></script><script src="//cdn.bootcss.com/fastclick/1.0.6/fastclick.min.js"></script><script src="//cdn.bootcss.com/jquery_lazyload/1.9.7/jquery.lazyload.min.js"></script><script src="/blog/lib/velocity/velocity.min.js?v=1.2.1"></script><script src="/blog/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script src="//cdn.bootcss.com/fancybox/3.5.6/jquery.fancybox.min.js"></script><script src="/blog/js/utils.js?v=7.1.2"></script><script src="/blog/js/motion.js?v=7.1.2"></script><script src="/blog/js/affix.js?v=7.1.2"></script><script src="/blog/js/schemes/pisces.js?v=7.1.2"></script><script src="/blog/js/scrollspy.js?v=7.1.2"></script><script src="/blog/js/post-details.js?v=7.1.2"></script><script src="/blog/js/next-boot.js?v=7.1.2"></script><script src="//cdn.jsdelivr.net/npm/leancloud-storage@3/dist/av-min.js"></script><script src="//cdn.jsdelivr.net/npm/valine@1/dist/Valine.min.js"></script><script>var GUEST=["nick","mail","link"],guest="nick,mail,link";guest=guest.split(",").filter(function(e){return GUEST.indexOf(e)>-1}),new Valine({el:"#comments",verify:!1,notify:!1,appId:"fIXqVCTX6VryzoR8ENkLXThu-gzGzoHsz",appKey:"nvWdXA0OQKn58RCTSMmd5JAK",placeholder:"欢迎交流讨论...",avatar:"mm",meta:guest,pageSize:"10",visitor:!0,lang:"zh-cn"})</script><script>function proceedsearch(){$("body").append('<div class="search-popup-overlay local-search-pop-overlay"></div>').css("overflow","hidden"),$(".search-popup-overlay").click(onPopupClose),$(".popup").toggle();var e=$("#local-search-input");e.attr("autocapitalize","none"),e.attr("autocorrect","off"),e.focus()}var isfetched=!1,isXml=!0,search_path="search.xml";0===search_path.length?search_path="search.xml":/json$/i.test(search_path)&&(isXml=!1);var path="/blog/"+search_path,onPopupClose=function(e){$(".popup").hide(),$("#local-search-input").val(""),$(".search-result-list").remove(),$("#no-result").remove(),$(".local-search-pop-overlay").remove(),$("body").css("overflow","")},searchFunc=function(e,t,o){"use strict";$("body").append('<div class="search-popup-overlay local-search-pop-overlay"><div id="search-loading-icon"><i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i></div></div>').css("overflow","hidden"),$("#search-loading-icon").css("margin","20% auto 0 auto").css("text-align","center"),$.ajax({url:e,dataType:isXml?"xml":"json",async:!0,success:function(e){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var n=isXml?$("entry",e).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get():e,r=document.getElementById(t),s=document.getElementById(o),a=function(){var e=r.value.trim().toLowerCase(),t=e.split(/[\s\-]+/);t.length>1&&t.push(e);var o=[];if(e.length>0&&n.forEach(function(n){function r(t,o,n,r){for(var s=r[r.length-1],a=s.position,i=s.word,l=[],h=0;a+i.length<=n&&0!=r.length;){i===e&&h++,l.push({position:a,length:i.length});var p=a+i.length;for(r.pop();0!=r.length&&(s=r[r.length-1],a=s.position,i=s.word,p>a);)r.pop()}return c+=h,{hits:l,start:o,end:n,searchTextCount:h}}function s(e,t){var o="",n=t.start;return t.hits.forEach(function(t){o+=e.substring(n,t.position);var r=t.position+t.length;o+='<b class="search-keyword">'+e.substring(t.position,r)+"</b>",n=r}),o+=e.substring(n,t.end)}var a=!1,i=0,c=0,l=n.title.trim(),h=l.toLowerCase(),p=n.content.trim().replace(/<[^>]+>/g,""),u=p.toLowerCase(),f=decodeURIComponent(n.url).replace(/\/{2,}/g,"/"),d=[],g=[];if(""!=l&&(t.forEach(function(e){function t(e,t,o){var n=e.length;if(0===n)return[];var r=0,s=[],a=[];for(o||(t=t.toLowerCase(),e=e.toLowerCase());(s=t.indexOf(e,r))>-1;)a.push({position:s,word:e}),r=s+n;return a}d=d.concat(t(e,h,!1)),g=g.concat(t(e,u,!1))}),(d.length>0||g.length>0)&&(a=!0,i=d.length+g.length)),a){[d,g].forEach(function(e){e.sort(function(e,t){return t.position!==e.position?t.position-e.position:e.word.length-t.word.length})});var v=[];0!=d.length&&v.push(r(l,0,l.length,d));for(var $=[];0!=g.length;){var C=g[g.length-1],m=C.position,x=C.word,w=m-20,y=m+80;w<0&&(w=0),y<m+x.length&&(y=m+x.length),y>p.length&&(y=p.length),$.push(r(p,w,y,g))}$.sort(function(e,t){return e.searchTextCount!==t.searchTextCount?t.searchTextCount-e.searchTextCount:e.hits.length!==t.hits.length?t.hits.length-e.hits.length:e.start-t.start});var T=parseInt("1");T>=0&&($=$.slice(0,T));var b="";b+=0!=v.length?"<li><a href='"+f+"' class='search-result-title'>"+s(l,v[0])+"</a>":"<li><a href='"+f+"' class='search-result-title'>"+l+"</a>",$.forEach(function(e){b+="<a href='"+f+'\'><p class="search-result">'+s(p,e)+"...</p></a>"}),b+="</li>",o.push({item:b,searchTextCount:c,hitCount:i,id:o.length})}}),1===t.length&&""===t[0])s.innerHTML='<div id="no-result"><i class="fa fa-search fa-5x"></i></div>';else if(0===o.length)s.innerHTML='<div id="no-result"><i class="fa fa-frown-o fa-5x"></i></div>';else{o.sort(function(e,t){return e.searchTextCount!==t.searchTextCount?t.searchTextCount-e.searchTextCount:e.hitCount!==t.hitCount?t.hitCount-e.hitCount:t.id-e.id});var a='<ul class="search-result-list">';o.forEach(function(e){a+=e.item}),a+="</ul>",s.innerHTML=a}};r.addEventListener("input",a),$(".local-search-pop-overlay").remove(),$("body").css("overflow",""),proceedsearch()}})};$(".popup-trigger").click(function(e){e.stopPropagation(),isfetched===!1?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(onPopupClose),$(".popup").click(function(e){e.stopPropagation()}),$(document).on("keyup",function(e){var t=27===e.which&&$(".search-popup").is(":visible");t&&onPopupClose()})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script><script type="text/javascript" src="/blog/js/core_socialist_values.js"></script><script src="/blog/live2d/lib/L2Dwidget.min.js?094cbace49a39548bed64abff5988b05"></script><script>L2Dwidget.init({pluginRootPath:"live2d/",pluginJsPath:"lib/",pluginModelPath:"assets/",tagMode:!1,debug:!1,model:{jsonPath:"/blog/live2d/assets/assets/haru02.model.json"},display:{position:"left",superSample:1,width:150,height:300,hOffset:10,vOffset:10},mobile:{show:!0},react:{opacityDefault:1,opacityOnHover:.2},log:!1})</script></body></html><!-- rebuild by neat -->